using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;

using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Permissions;


namespace Open.Data.Persistence.Tools
{
    public class ListviewEx: ListView 
    {
        private ImageList _imagelistHeight;
        private List<Bitmap> _images;

        private bool m_doubleClickDoesCheck = false;       
        private bool m_inDoubleClickCheckHack = false;

        public ListviewEx()
        {
            DoubleBuffered = true;

            //Force the row height
            _imagelistHeight = new ImageList();
            _imagelistHeight.ImageSize = new Size(20, 20);

            SmallImageList = _imagelistHeight;

            //Set up owner draw
            OwnerDraw = true;
            DrawColumnHeader += new DrawListViewColumnHeaderEventHandler(listView1_DrawColumnHeader);
            DrawItem += new DrawListViewItemEventHandler(listView1_DrawItem);
            DrawSubItem += new DrawListViewSubItemEventHandler(listView1_DrawSubItem);

            //Load up images
            _images = new List<Bitmap>();

            LoadImage("Resource.exclaim.bmp");
            LoadImage("Resource.progress.bmp");
            LoadImage("Resource.tick.bmp");
            LoadImage("Resource.cross.bmp");
        }

        public List<Bitmap> Images
        {
            get
            {
                return _images;
            }
        }

        //****************************************************************************************        
        // This function helps us overcome the problem with the managed listview wrapper wanting        
        // to turn double-clicks on checklist items into checkbox clicks.  We count on the fact        
        // that the base handler for NM_DBLCLK will send a hit test request back at us right away.        
        // So we set a special flag to return a bogus hit test result in that case.        
        //****************************************************************************************        
        private unsafe void OnWmReflectNotify(ref Message m)        
        {            
            if (!DoubleClickDoesCheck && CheckBoxes)            
            {                
                NativeMethods.NMHDR* nmhdr = (NativeMethods.NMHDR *)m.LParam;                 
                if (nmhdr->code == NativeMethods.NM_DBLCLK)                
                {                    
                    m_inDoubleClickCheckHack = true;                
                }            
            }        
        }         
        
        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]        
        protected override void WndProc(ref Message m)        
        {            
            switch (m.Msg)            
            {               
                //  This code is to hack around the fact that the managed listview                
                //  wrapper translates double clicks into checks without giving the                 
                //  host to participate.                
                //  See OnWmReflectNotify() for more details.                
                case NativeMethods.WM_REFLECT + NativeMethods.WM_NOTIFY:                    
                    OnWmReflectNotify(ref m);                    
                    break;                 
                //  This code checks to see if we have entered our hack check for                
                //  double clicking items in check lists.  During the NM_DBLCLK                
                //  processing, the managed handler will send a hit test message                
                //  to see which item to check.  Returning -1 will convince that                
                //  code not to proceed.                
                case NativeMethods.LVM_HITTEST:                    
                    if (m_inDoubleClickCheckHack)                    
                    {                        
                        m_inDoubleClickCheckHack = false;                        
                        m.Result = (System.IntPtr)(-1);                        
                        return;                    
                    }                    
                    break;            
            }             
            base.WndProc(ref m);        
        }         
        
        [Browsable(true), Description("When CheckBoxes is true, this controls whether or not double clicking will toggle the check."), Category("My Controls"), DefaultValue(true)]        
        public bool DoubleClickDoesCheck        
        {            
            get            
            {                
                return m_doubleClickDoesCheck;            
            }             
            set            
            {                
                m_doubleClickDoesCheck = value;            
            }        
        }    

        private void LoadImage(string path)
        {
            Bitmap bmp = new Bitmap(GetType(), path);
            bmp.MakeTransparent(Color.FromArgb(255, 0, 255));
            _images.Add(bmp);
        }

        private void listView1_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
        {
            e.DrawDefault = true;
        }

        private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e)
        {
            //Checkbox, so dont draw anything
            object x = e;
        }

        //Only fires if draw item is owner drawn
        private void listView1_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
        {
            //Draw image and text if column 1
            if (e.ColumnIndex == 1)
            {
                SolidBrush brush = null;

                if (e.Item.Selected && Focused)
                {
                    brush = new SolidBrush(System.Drawing.SystemColors.Highlight);
                    e.Graphics.FillRectangle(brush, e.Bounds);
                    brush = new SolidBrush(System.Drawing.SystemColors.HighlightText);
                }
                else
                {
                    brush = new SolidBrush(e.SubItem.ForeColor);
                    e.DrawBackground();
                }

                //Draw the image located in the tag
                int index = 0;

                if (e.SubItem.Text == "In Progress") index = 1;
                if (e.SubItem.Text == "Deployed" || e.SubItem.Text == "Validated") index = 2;
                if (e.SubItem.Text == "Exception" || e.SubItem.Text == "Cancelled") index = 3;

                Point location = e.Bounds.Location;

                //Draw the image
                location.Offset(2, 2);
                //e.Graphics.DrawImageUnscaled(imageList1.Images[index], location);
                e.Graphics.DrawImageUnscaled(_images[index], location);

                location.Offset(18, 2);
                e.Graphics.DrawString(e.SubItem.Text, Font, brush, location);

            }
            else
            {
                e.DrawDefault = true;
            }
        }
    }

    //****************************************************************************************    
    //  This is stuff you would normally put in a separate file with all the other interop    
    //  you have to work with.    
    //****************************************************************************************    
    public class NativeMethods    
    {        
        public const int WM_USER = 0x0400;       
        public const int WM_REFLECT = WM_USER + 0x1C00;        
        public const int WM_NOTIFY = 0x004E;        
        public const int LVM_HITTEST = (0x1000 + 18);        
        public const int NM_DBLCLK = (-3);         
        [StructLayout(LayoutKind.Sequential)]        
        public struct NMHDR        
        {            
            public IntPtr hwndFrom;            
            public UIntPtr idFrom;            
            public int code;        
        }    
    }
}
